#pragma once

#define forceinline __forceinline

namespace krryn{
namespace system_imp{
	typedef int fp_type; // fp_type is assumed to be 32 bit
	typedef long long fp_next_type; // fp_next_type is assumed to be 64 bit

	template<typename _Type>
	struct fp_values{
		forceinline static _Type one(){ return 1; }
		forceinline static _Type zero(){ return 0; }
		forceinline static _Type half(){ return 0.5; }
	};

	template<typename _Type>
	forceinline _Type cast_to(float a_Value){
		return (_Type)a_Value;
	}

	template<typename _Type>
	forceinline _Type fraction(_Type a_Value){
		int l_Temp = (int)a_Value;
		return a_Value - (_Type)l_Temp;
	}

	template<typename _Type>
	forceinline int fp_ceil(_Type a_Value){
		return (int)ceil(a_Value);
	}

	template<typename _Type>
	forceinline int to_int(_Type a_Value){
		return (int)a_Value;
	}

	template<typename _Type>
	forceinline _Type mul_int(const _Type a_Right, const int a_Left){
		return a_Right * (_Type)a_Left;
	}

	template<typename _Type>
	forceinline _Type sub_int(const int a_Right, const _Type a_Left){
		return a_Right - a_Left;
	}

	template<typename _Type>
	forceinline _Type mul(const _Type a_Right, const _Type a_Left){
		return a_Left * a_Right;
	}

	template<typename _Type>
	forceinline _Type div(const _Type a_Left, const _Type a_Right){
		return a_Left / a_Right;
	}

	template<typename _Type>
	forceinline _Type inverse(_Type a_Value){
		return ((_Type)1) / a_Value;
	}

	template<typename _Type>
	forceinline int __fastcall log2(_Type a_A){
		return (int)log(a_A, 2);
	}

	template<typename _Type>
	forceinline _Type det_2x2(_Type a_A, _Type a_B, _Type a_C, _Type a_D){
		return a_A * a_D - a_B * a_C;
	}

	template<typename _Type>
	forceinline _Type lerp(_Type a_A, _Type a_B, _Type a_T){
		return a_B + mul<_Type>(a_A - a_B, a_T);
	}

	template<>
	int __fastcall log2<float>(float a_Value){
		int log = *(int*)&a_Value;
		log >>= 23;
		log -= 127;
		return log;
	}

	template<typename _Type>
	forceinline _Type persp_bias(_Type value){
		return value * 1000.f;
	}

	template<typename _Type>
	forceinline _Type persp_unbias(_Type value){
		return value / 1000.f;
	}

	/// integer (12:20 fixed point) implementation starts here

	// 18 <= SHIFT_FACTOR <= 21 seems to work OK
	#define SHIFT_FACTOR 20U

	template<>
	struct fp_values<fp_type>{
		forceinline static int one(){ return 1 << SHIFT_FACTOR; }
		forceinline static int zero(){ return 0 << SHIFT_FACTOR; }
		forceinline static int half(){ return 1 << (SHIFT_FACTOR - 1); }
	};

	template<>
	forceinline fp_type persp_bias<fp_type>(fp_type value){
		return value << 8;
	}

	template<>
	forceinline fp_type persp_unbias<fp_type>(fp_type value){
		return value >> 8;
	}

	template<>
	forceinline fp_type cast_to<fp_type>(float a_Value){
		return (fp_type)(a_Value * (1 << SHIFT_FACTOR));
	}

	template<>
	forceinline int __fastcall log2<fp_type>(fp_type a_Value){
		__asm sar ecx, SHIFT_FACTOR;
		__asm bsr ecx, ecx;
		__asm mov eax, ecx;
	}

	template<>
	forceinline fp_type det_2x2<fp_type>(fp_type a_A, fp_type a_B, fp_type a_C, fp_type a_D){
		fp_next_type l_Values[] = {
			(fp_next_type)a_A * (fp_next_type)a_D,
            (fp_next_type)a_B * (fp_next_type)a_C
		};
		
		return (fp_type)((l_Values[0] - l_Values[1])  >> SHIFT_FACTOR);
    }

	template<>
	forceinline fp_type fraction<fp_type>(fp_type a_Value){
		return (a_Value & ((1 << SHIFT_FACTOR) - 1));
	}

	template<>
	forceinline int to_int(fp_type a_Value){
		return (int)(a_Value >> SHIFT_FACTOR);
	}

	template<>
	forceinline int fp_ceil(fp_type a_Value){
		a_Value += ((1 << SHIFT_FACTOR) - 1);
		return a_Value >> SHIFT_FACTOR;
	}

	template<>
	forceinline fp_type sub_int(const int a_Right, const fp_type a_Left){
		return (a_Right << SHIFT_FACTOR) - a_Left;
	}

	template<>
	forceinline fp_type mul<fp_type>(const fp_type a_Left, const fp_type a_Right){
		return (fp_type)(((fp_next_type)a_Left * a_Right) >> SHIFT_FACTOR);
	}

	template<>
	forceinline fp_type mul_int<fp_type>(const fp_type a_Left, const int a_Right){
		return mul<fp_type>(a_Left, a_Right << SHIFT_FACTOR);
	}

	template<>
	forceinline fp_type div<fp_type>(const fp_type a_Left, const fp_type a_Right){
		return (fp_type)((((fp_next_type)a_Left) << SHIFT_FACTOR) / a_Right);
	}

	template<>
	forceinline fp_type inverse<fp_type>(fp_type a_Value){
		return div<fp_type>(fp_values<fp_type>::one(), a_Value);
	}

	
	// First off a simple explanation about how floating points numbers are 
	// stored. Remember scientific notation from high school? 1.34e10 ring a 
	// bell? Well, floating point numbers work kinda similar. Except, they use 
	// base 2 instead of base 10, so they are stored as 2^n * 0.29834. You 
	// can't use n directly though, you have to subtract 127 before it's useful.
	// 
	// However, this has a very nice property. We can now, quite easily get the 
	// integer log2(n) of any floating point number. We simply have to extract n
	// somehow. That's easy enough, right? Just ditch the mantissa (eg. the 
	// right most 23 bits) and subtract 127.
	// 
	//		int log2(float a_Float){
	//			int log = *(int*)&a_Float;
	//			log >>= 23;
	//			log -= 127;
	//			return log;
	//		}
	//	 
	// There are other things you can do with this such as finding the next 
	// power of two (just increment n by 1).
	// 
	// This is one part of the mip-map calculation done. The entire formula that
	// I'm sticking to is this:
	// 
	//		int level = log2(sqrt(area));
	// 
	// The next problem is tackling the sqrt part. However, it can be shown that
	// we can write sqrt(n) in terms of log2.
	// 
	// First off, as everyone knows,
	// 
	//		sqrt(n) = n ^ (1/2)
	// 
	// And also:
	// 
	//		n = b ^ (log(n) / log(b)) //  b = base
	// 
	// So:
	// 
	//		n ^ (1/2) = 2 ^ (log(n ^ (1/2)) / log(2))
	// 
	// However
	//		log(n ^ p) / log(b) = p * (log(n) / log(b))
	// 
	// So we can reduce the previous expression to:
	//		n ^ (1/2) = 2 ^ (1/2 * (log(n) / log(2)))
	// 
	// So now we have one awesome expression with:
	// 
	// - 2 ^ something
	// - something / 2
	// - log2(something)
	// 
	// This can be written as:
	// 
	//		int a = 1 >> something; //  2^n
	//		a >> 1; //  n / 2
	// 
	// And a log2(n) function. We already have one of those for floating point
	// numbers, but we need one for integers as well (remember, fixed point!).
	// 
	// Thankfully, though, log2(n) for integers can be done in one instruction
	// on the x86! The BSR instruction :-). What that instruction does is count
	// the number of bits that are set. How is that log2(n)?
	// 
	// Well, every number can be expressed as a power of it's base. Eg.
	// 
	//		b101 = (2 ^ 2 * 1) + (2 ^ 1 * 0) + (2 ^ 0 * 1)
	// 
	// So 2^n * v. BSR returns the last n that is 1, which is exactly the same
	// as the log with base two!
	// 
	// So, we've solved the puzzle :-) we can now calculate log2(sqrt(n)) in 5
	// instructions! (BSR, SAR, MOV, SHL and BSR again)
	template<typename _Type>
	forceinline int __fastcall mip_map(_Type a_Area){
		return (int)(log(sqrtf(a_Area), 2));
	}

	//template<>
	//forceinline int __fastcall mip_map<float>(float a_Area){
	//	__asm mov ecx, a_Area;

	//	// integer log2(n)
	//	// int log2 = *(int*)&a_Area;
	//	// log2 >>= 23;
	//	// log2 -= 127;
	//	__asm sar ecx, 23;
	//	__asm sub ecx, 127;

	//	// sqrt(n) = 2 ^ (.5 * log2(n))
	//	// int sqrt = (1 << (log2 >> 1));
	//	__asm sar ecx, 1;
	//	__asm mov eax, 1;
	//	__asm shl eax, cl;

	//	// integer log2(n) again
	//	__asm bsr eax, eax;
	//}

	//template<>
	//forceinline int __fastcall mip_map<fp_type>(fp_type a_Area){
	//	// TODO: decide if this function shifts fixed point, or the caller

	//	// integer log2(n)
	//	__asm bsr ecx, ecx;

	//	// sqrt(n) = 2 ^ (.5 * log2(n))

	//	// integer sqrt (notice, off by quite a percentage!)
	//	__asm sar ecx, 1;
	//	__asm mov eax, 1;
	//	__asm shl eax, cl;

	//	// previous error doesn't really matter
	//	// because we log2(n) again

	//	// another log2(n)
	//	__asm bsr eax, eax;
	//}
}
}
